/*
 * $Id: CHFTransaction.java,v 1.11 2012/01/26 15:34:22 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.iptds.transaction;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import org.springframework.util.Assert;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.database.dao.IRequestTypeDAO;
import com.ge.healthcare.autosc.common.database.pojo.ActRequest;
import com.ge.healthcare.autosc.common.database.pojo.ActRequestList;
import com.ge.healthcare.autosc.common.database.pojo.ActTask;
import com.ge.healthcare.autosc.common.dbLogger.ContactLogger;
import com.ge.healthcare.autosc.common.message.sender.MessageSender;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.common.util.TimeFormatStandard;
import com.ge.healthcare.autosc.processor.IPTDSCodeConstants;
import com.ge.healthcare.autosc.processor.exception.IPTDSException;
import com.ge.healthcare.autosc.processor.iip.comm.ClientDrvCommand;
import com.ge.healthcare.autosc.processor.iptds.IPTDSSystemInfo;

public class CHFTransaction extends BaseTransaction implements ICHFTransaction {

	private ITHFTransaction iTHFTransaction = null;
	private IRequestTypeDAO requestTypeDAO = null;
	private MessageSender messageSender = null;

	public CHFTransaction() {
	}

	/**
	 * Init method. In spring framework, it is used as customized init method
	 */
	public void init() {
		super.init();
		Assert.notNull(requestTypeDAO, "requestTypeDAO object is not defined. "
				+ "Please use setRequestTypeDAO to provide one");
		Assert.notNull(iTHFTransaction,
				"THFTransaction object is not defined. "
						+ "Please use setTHFTransaction to provide one");
		Assert.notNull(messageSender,
				"messageSender is not defined. Use setMessageSender to provide one");
	}

	/**
	 * Execute CHF level transaction. It retrieves CHF file, parse CHF file and
	 * pass THF transaction request to THFTransaction. THFTransaction is
	 * expected to throw exception back only if the error is showstopper to the
	 * IPTDS process. In this case, it will stop processing other THFs being
	 * followed.
	 * <p>
	 * As long as there is successfully processed THF with request and task in
	 * the AutoSC, this method will schedule against created request for post
	 * processing event.
	 * <p>
	 * The method has transaction type request_new. Please refer to its
	 * interface.
	 * 
	 * @param batchId
	 *            - Batch Id to group IPTDS transaction. If this value is null,
	 *            it means this is the first CHF in the current IPTDS
	 *            transaction.
	 * @param systemInfo
	 *            - Holding all remote system information.
	 * @param clientDrv
	 *            - ClientDrvCommand
	 * @return Batch Id if it is batch mode, or null if either CHF is not in
	 *         batch mode or no more CHF file to process.
	 */
	public Long executeTransaction(Long batchId, IPTDSSystemInfo systemInfo,
			ClientDrvCommand clientDrv) throws IPTDSException {
		String methodName = "executeTransaction";
		Long newBatchId = null;
		ChfFile chfFile = null;
		ActRequestList requestList = null;

		ASCLogger.info(
				this.getClass(),
				methodName,
				"Execute CHF transaction for system "
						+ systemInfo.getSystemId());
		boolean checkEntitlement = ApplicationInfo.getInstance()
				.getBooleanProperty(
						PropertyConstants.PERFORM_ENTITLEMENT_CHECK, "true");
		if (checkEntitlement && !systemInfo.isEntitled()) {
			ASCLogger.info(this.getClass(), "executeTransaction",
					"Denying access to unentitled system:" + systemInfo);
			return -1L;
		}
		/*
		 * Fetch a new batch Id for file transfer. We do not persist
		 * ActRequestList here but in the THF transaction.
		 */
		try {
			requestList = createRequestList(batchId);
			chfFile = getCHF(batchId, systemInfo, clientDrv, requestList);
		} catch (IPTDSException iptdse) {
			String errMsg = "Failed getting CHF File. Reason: "
					+ iptdse.getMessage();
			ContactLogger.addContactLog(errMsg);
			ASCLogger.error(this.getClass(), methodName, errMsg);
			throw new IPTDSException(errMsg.toString(), iptdse,
					iptdse.getErrorCode(), iptdse.showStopper());
		} catch (Exception e) {
			String errMsg = "Failed processing CHF. Reason: " + e.getMessage();
			ContactLogger.addContactLog(errMsg);
			ASCLogger.fatal(this.getClass(), methodName, errMsg, e);
			throw new IPTDSException(errMsg.toString(), e,
					IPTDSCodeConstants.PRC_F_TDS_019);
		}

		try {
			newBatchId = processChf(chfFile, requestList, systemInfo, clientDrv);
		} finally {
			if (requestList.getRequests().size() > 0) {
				// Schedule all created request
				scheduleTask(requestList);
			}
		}
		return newBatchId;
	}

	private Long processChf(ChfFile chfFile, ActRequestList requestList,
			IPTDSSystemInfo systemInfo, ClientDrvCommand clientDrv)
			throws IPTDSException {
		String methodName = "processChf";
		Long newBatchId = null;
		boolean iptdsTranStatus = true;

		if (chfFile.isLoaded() == true) {
			if (chfFile.isBatchMode()) {
				ASCLogger.info(
						this.getClass(),
						methodName,
						"in batch mode.. get batchId "
								+ requestList.getBatchId());
				newBatchId = requestList.getBatchId();
			}

			// Walk through THFs in the CHF and process each THF transaction
			Enumeration<ThfProperties> thfEnum = chfFile
					.elements(ChfFile.TDF_KEY);
			while (thfEnum.hasMoreElements()) {
				ThfProperties thf = (ThfProperties) thfEnum.nextElement();
				if (iTHFTransaction.executeTransaction(requestList, chfFile,
						thf, systemInfo, clientDrv) == false) {
					iptdsTranStatus = false;
				}
			}

			if (!iptdsTranStatus) {
				throw new IPTDSException("Failed processing one/more THFs",
						IPTDSCodeConstants.PRC_F_TDS_020);
			}
		} else {
			ASCLogger.info(this.getClass(), methodName,
					"no more CHF file to process");
		}
		return newBatchId;
	}

	public void setRequestTypeDAO(IRequestTypeDAO requestTypeDAO) {
		this.requestTypeDAO = requestTypeDAO;
	}

	public void setTHFTransaction(ITHFTransaction thfTransaction) {
		this.iTHFTransaction = thfTransaction;
	}

	/**
	 * Set JMS message sender
	 */
	public void setMessageSender(MessageSender messageSender) {
		this.messageSender = messageSender;
	}

	/*
	 * Create ActRequestList object. This method does not call save and fetch
	 * request list Id manually.
	 */
	private ActRequestList createRequestList(Long batchId) {

		if (batchId == null) {
			batchId = requestListDAO.getNewBatchId();
			ASCLogger.debug(this.getClass(), "createRequestList",
					"get New batch Id: " + batchId);
		}
		ActRequestList requestList = new ActRequestList();
		requestList.setBatchId(batchId);
		requestList.setRequestTypeId(requestTypeDAO.getRequestTypeByName(
				"IPTDS").getRequestTypeId());
		requestList.setEnterDate(TimeFormatStandard.getNowMillis());
		requestList.setScheduledDate(TimeFormatStandard.getNowMillis());
		requestList.setValidCount(new Long(0));
		requestList.setInvalidCount(new Long(0));
		return requestList;
	}

	/*
	 * Get CHF file from remote system to the Back office
	 */
	private ChfFile getCHF(Long batchId, IPTDSSystemInfo systemInfo,
			ClientDrvCommand clientDrv, ActRequestList requestList)
			throws IPTDSException {
		ChfFile chf = new ChfFile();
		IPTDSFTPCommand ftpCommand = systemInfo.getFTPCommand();
		String errMsg = new StringBuffer()
				.append("Failed getting CHF ready for IPTDS transaction. ")
				.append("Please refer to exception.").toString();

		String jobDirStr = ASCUtil.getJobDir(requestList.getBatchId());
		File jobDir = new File(jobDirStr);
		File remoteFile = new File(systemInfo.getRemoteFilePathFromArg());
		File localFile = new File(jobDir, remoteFile.getName());

		try {
			if (!jobDir.exists()) {
				// It needs to be thread safe
				synchronized (CHFTransaction.class) {
					if (jobDir.mkdirs() == false) {
						throw new IPTDSException(errMsg,
								IPTDSCodeConstants.PRC_F_TDS_016, true);
					}
				}
			}

			if (batchId == null) {
				// First time processing the chf file, regardless of batch mode
				// or not.
				localFile = new File(jobDir, remoteFile.getName());
				ftpCommand.getRemoteFile(localFile, remoteFile, clientDrv);
			} else {
				localFile = File.createTempFile("raw", ".chf", jobDir);
				if (ftpCommand.getMoreCHFFile(localFile, clientDrv) == false) {
					// delete the empty temp file
					localFile.delete();
					this.cleanDir(jobDir);
					return chf;
				}
			}
			chf.addLog("Successfully get remote File " + remoteFile.getPath()
					+ " to back office " + localFile.getPath());
			loadCHF(chf, localFile, systemInfo);
		} catch (IPTDSException iptdse) {
			throw new IPTDSException(errMsg,
					iptdse.hasErrorCode() ? iptdse.getErrorCode()
							: IPTDSCodeConstants.PRC_F_TDS_011,
					iptdse.showStopper(), iptdse);
		} catch (IOException ioe) {
			throw new IPTDSException(errMsg, ioe,
					IPTDSCodeConstants.PRC_F_TDS_011, true);
		}
		return chf;
	}

	private void loadCHF(ChfFile chf, File localFile, IPTDSSystemInfo systemInfo)
			throws IPTDSException {
		try {
			chf.loadFile(localFile);
			chf.validate(systemInfo.getUsn());
		} catch (IOException ioe) {
			String msg = "Failed loading CHF file into ChfFile object";
			ContactLogger.addContactLog(msg);
			throw new IPTDSException(msg, ioe,
					IPTDSCodeConstants.PRC_F_TDS_011, true);
		}
	}

	private void cleanDir(File chfDir) {
		if (chfDir.exists()) {
			String[] contents = chfDir.list();
			if (contents.length == 0) {
				chfDir.delete();
			}
		}
	}

	/*
	 * Schedule the task to trigger the next task after the file transfer
	 */
	private void scheduleTask(ActRequestList requestList) {
		int invalidRequest = 0;
		if (requestList != null) {
			List<ActRequest> requests = requestList.getRequests();
			Iterator<ActRequest> requestIter = requests.iterator();
			while (requestIter.hasNext()) {
				ActRequest request = requestIter.next();
				if (this.isRequestValid(request)) {
					List<ActTask> tasks = request.getActTasks();
					if (!ASCUtil.isNullOrEmptyList(tasks)) {
						ActTask task = request.getActTasks().get(0);
						messageSender.sendToQueue(task);
					}
				} else {
					invalidRequest++;
				}
			}

			// If all requests are invalid, only sent one request to close the
			// requestList.
			// DO NOT SEND MORE THAN ONE REQUEST OR IT WILL BLOW THE DSP.
			if ((invalidRequest > 0) && (invalidRequest == requests.size())) {
				messageSender.sendToQueue(requests.get(0));
			}
		}
	}
}
